/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.niir.goodfil.ui;

import org.apache.log4j.Logger;
import ru.niir.goodfil.db.*;
import ru.niir.goodfil.db.dic.DeliveryType;
import ru.niir.goodfil.db.dic.OrderState;
import ru.niir.goodfil.exceptions.*;
import ru.niir.goodfil.fun.ISSignature;
import ru.niir.goodfil.utils.validation.CreateCheckResult;
import ru.niir.goodfil.utils.validation.ValidationError;
import ru.niir.goodfil.utils.validation.ValidationResult;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static ru.niir.goodfil.ui.Utils.setReservationDate;

/**
 * Регистрация клиента.
 *
 * @author Носов А.В.
 */
public class ClientRegistrationBean extends PageBean {
    private static Logger logger = Logger.getLogger(ClientRegistrationBean.class);

    /**
     * Клиент
     */
    private Client client = new Client();
    /***/
    private String remoteAddr;
    /**
     * Идентификатор заказа
     */
    private long orderId;
    /**
     * Проверка email
     */
    public static final Pattern email = Pattern.compile("^[-a-z0-9!#$%&'*+/=?^_`{|}~]+(?:\\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*@(?:[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?\\.)*(?:aero|arpa|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|[a-z][a-z])$");
    /**
     * Проверка Url
     */
    //public static final Pattern urlSite = Pattern.compile("/^(https?:\\/\\/)?(([0-9a-z_!~*'().&=+$%-]+:)?[0-9a-z_!~*'().&=+$%-]+@)?(([0-9]{1,3}\\.){3}[0-9]{1,3}|([0-9a-z_!~*'()-]+\\.)*([0-9a-z][0-9a-z-]{0,61})+[0-9a-z]\\.[a-z]{2,6})(:[0-9]{1,4})?((\\/?)|(\\/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+\\/?)$/");
    public static final Pattern urlSite = Pattern.compile("/^(http:\\/\\/|https:\\/\\/)?([^\\.\\/]+\\.)*([a-zA-Z0-9])([a-zA-Z0-9-]*)\\.([a-zA-Z]{2,4})(\\/.*)?$/i");
    /**
     * Проверка телефона
     */
    public static final Pattern phone = Pattern.compile("(([\\d]{1})([\\-]{0,1})([\\d]{3})([\\-]{0,1})([\\d]{2,3})([\\-]{0,1})([\\d]{2})([\\-]{0,1})([\\d]{2,3}))|(([\\d]{2,3})([\\-]{0,1})([\\d]{2})([\\-]{0,1})([\\d]{2}))");

    public ClientRegistrationBean() throws IOException, ServletException {
        //
        //  Very simple redirect нах
        //
//        if (getSessionBean().getSelectedClientId() == -1)
//        {
//            HttpServletResponse response = (HttpServletResponse) getFacesContext().getExternalContext().getResponse();
//            response.sendRedirect("ClientGoodsPage.jsf");
//            getFacesContext().responseComplete();
//        }
    }

    /**
     * Возвращает клиента.
     *
     * @return клиент
     */
    public Client getClient() {
        if (getSessionBean().getSelectedClientId() != -1) {
            client = gf().getClientById(getSessionBean().getSelectedClientId());
//            client.setClientGroup(gf().getClientGroupById(client.getClientGroupId()));
        }
        return client;
    }

    /**
     * Возвращает идентификатор заказа.
     *
     * @return идентификатор
     */
    public long getOrderId() {
        return orderId;
    }

    /**
     * Устанавливает идентификатор заказа.
     *
     * @param orderId идентификатор
     */
    public void setOrderId(long orderId) {
        this.orderId = orderId;
    }

    /**
     * Возвращает исторю заказов.
     *
     * @return спиок заказов
     */
    public List<Order> getOrders() {
        int deliveryCompanyMoney = gf().getInt("delivery.company.money");
        int deliveryFreeMoney = gf().getInt("delivery.free.money");
        int deliveryEdge = gf().getInt("delivery.edge");

        List<Order> orders = new ArrayList<Order>();
        if (getSessionBean().getSelectedClientId() != -1) {
            orders = gf().getOrdersByClientId(getSessionBean().getSelectedClientId());
            // Вытаскиваем последние пять
            Collections.sort(orders, new Comparator<Order>() {
                public int compare(Order o1, Order o2) {
                    return o1.getCreationDate().compareTo(o2.getCreationDate());
                }
            });
            // По идее вконце они
            List<Order> o = new ArrayList<Order>();
            if (orders.size() > 5) {
                for (int i = orders.size(); i > orders.size() - 5; i--) o.add(orders.get(i - 1));
                orders = o;
            }
        }

        for (Order order : orders) {
            List<OrderItem> orderItems = gf().getOrderItemsByOrderId(order.getId());

            float sum = 0;
            for (OrderItem orderItem : orderItems) sum += orderItem.getSum();
            order.setSum(sum);
            order.setPositionsCount(orderItems.size());

            float deliverySum = order.getDeliveryTypeId() == DeliveryType.COMPANY ? (sum >= deliveryEdge ? deliveryFreeMoney : deliveryCompanyMoney) : 0;
            order.setDeliverySum(deliverySum);
        }
        return orders;
    }

    /**
     * Возвращает заказы клиентов отфильтрованные по дате.
     *
     * @return список заказов
     */
    public List<Order> getOrdersList() {
        int deliveryCompanyMoney = gf().getInt("delivery.company.money");
        int deliveryFreeMoney = gf().getInt("delivery.free.money");
        int deliveryEdge = gf().getInt("delivery.edge");

        List<Order> orders = null;
        if (getSessionBean().getSelectedClientId() != -1) {
            getSessionBean().getOrdersFilter().setClientId(getSessionBean().getSelectedClientId());
            orders = gf().getOrders(0, Integer.MAX_VALUE, getSessionBean().getOrdersFilter());

            for (Order order : orders) {
                List<OrderItem> orderItems = gf().getOrderItemsByOrderId(order.getId());

                float sum = 0;
                for (OrderItem orderItem : orderItems) sum += orderItem.getSum();
                order.setSum(sum);
                order.setPositionsCount(orderItems.size());

                float deliverySum = order.getDeliveryTypeId() == DeliveryType.COMPANY ? (sum >= deliveryEdge ? deliveryFreeMoney : deliveryCompanyMoney) : 0;
                order.setDeliverySum(deliverySum);
            }
        }
        return orders;
    }

    /**
     * Изменить корзину.
     */
    public void selectedNewOrder() {
        if (getSessionBean().getSelectedOrderId() != orderId) {
            getSessionBean().setOrder(gf().getOrderById(orderId));
            try {
                getSessionBean().setOrderItems(gf().getOrderItemsByOrderId(orderId));
            } catch (RemoveFailureException ex) {
                logger.error(ex);
                ex.printStackTrace();
                String[] objs = ex.getRemoveCheckResult().getLinkedObjects();
                for (String obj : objs) {
                    facesError("Заказ нельзя удалить, т.к. он связан с другими объектами.");
                    break;
                }
            } catch (CreateFailureException ex) {
                logger.error(ex);
                ex.printStackTrace();
                CreateCheckResult createCheckResult = ex.getResult();
                facesError(createCheckResult.getMsg());
            } catch (ValidationException ex) {
                logger.error(ex);
                ex.printStackTrace();
                ValidationResult validationResult = ex.getResult();
                for (ValidationError error : validationResult.getErrors())
                    facesError(error.toHumanString());
            } catch (SettingNotFoundException ex) {
                logger.error(ex);
                ex.printStackTrace();
                facesError(ex.getSettingName());
            }
        }
    }

    /**
     * Активировать выбранную корзину.
     */
    public void activeOrder() {
        try {
            Order order = gf().getOrderById(orderId);

            List<OrderItem> oderItems = gf().getOrderItemsByOrderId(orderId);
            getSessionBean().setSelectedOrderId(orderId);
            getSessionBean().setOrder(order);
            getSessionBean().setOrderItems(oderItems);

            facesInfo("Заказ от " + order.getCreationDateAsString() + " выбран как текущий");
        } catch (Exception e) {
            logger.error(e);
            e.printStackTrace();

            facesError("Не удалось выбрать заказ");
        }
    }


    /**
     * -------------------------------------------------------------------------
     * ----------Работа с БД----------
     * -------------------------------------------------------------------------
     */

    /**
     * Регистрация пользователя.
     * Делается из ip и времени регистрации.
     */
    public void registration() {
        logger.debug("Registration...");

        client.setActive(false);

        String d = String.valueOf(Calendar.getInstance().getTime().getTime());
        String s = remoteAddr + d;
        StringBuffer code = new StringBuffer();
        String md5 = null;
        try {
            java.security.MessageDigest messageDigest = java.security.MessageDigest.getInstance("MD5");
            byte bytes[] = s.getBytes();
            byte digest[] = messageDigest.digest(bytes);
            for (int i = 0; i < digest.length; ++i)
                code.append(Integer.toHexString(0x0100 + (digest[i] & 0x00FF)).substring(1));
            md5 = code.toString();
        } catch (Exception e) {
            logger.error(e);
            e.printStackTrace();

            facesError("Не удалось провести регистрацию клиента");
        }

        if (hasFacesErrors()) return;

        logger.debug("Registration code: " + md5);
        client.setRegistrationCode(md5);
        for (Brand brand : gf().getBrands())
            client.getClientgroups().put(brand.getId(), gf().getMainClientGroupId2(brand.getId()));

        String tabNumber = gf().getOrCreateIfNotExists("default.manager", "Яковлев", "Менеджер для новых клиентов");
        User user = gf().getUserByTab(tabNumber);
        client.setManagerId(user.getId());

        try {
            gf().saveClient(client);
            facesInfo("Клиент сохранен");
        } catch (ValidationException ex) {
            logger.error(ex);
            ex.printStackTrace();
            ValidationResult validationResult = ex.getResult();
            for (ValidationError error : validationResult.getErrors())
                facesError(error.toHumanString());
        } catch (CreateFailureException ex) {
            logger.error(ex);
            ex.printStackTrace();
            CreateCheckResult createCheckResult = ex.getResult();
            facesError(createCheckResult.getMsg());
        }

        sendEmailConfirmationLetter(client, md5);
        sendManagerEmailConfirmationLetter(client);
    }

    private void sendEmailConfirmationLetter(Client client, String md5) {
        gf().createIfNotExists(".domain.name", "87.245.178.50:8180/Goodwill");
        String domainName = gf().get(".domain.name");

        String subject = "Регистрация в ИНТЕРНЕТ-системе Goodwill";
        String text = "Уважаемый " + client.getCompanyName() + "!\n\n" +
                "Для активации Вашей учетной записи в ИНТЕРНЕТ-системе Goodwill, пройдите пожалуйста по ссылке: \n" +
                "http://" + domainName + "/RegistrationPage.jsf?activation=" + md5 + "\n\n" +
                ISSignature.official();
        try {
            gf().sendEmailMsg(client.getContactEmail(), subject, text);
        } catch (MailerException ex) {
            logger.error(ex);
            ex.printStackTrace();
            facesError(ex.getError().getDescription());
        }
    }

    private void sendManagerEmailConfirmationLetter(Client client) {
        String tabNumber = gf().getOrCreateIfNotExists("default.manager", "Яковлев", "Менеджер для новых клиентов");
        User user = gf().getUserByTab(tabNumber);
        try {
            String subject = "Новый клиент: " + client.getCompanyName();
            String text = "Уважаемый " + user.getFio() + "!\n\n" +
                    "В ИНТЕРНЕТ-системе был зарегистрирован новый клиент: " + client.getCompanyName() + ". Этот клилент был назначен Вам (т.е. Вы являетесь его менедежром), т.к. Вы указаны в настройках ИНТЕРНЕТ-системы как менеджер, который назначается новым клиентам.\n" +
                    "Если Вы считаете, что данное письмо пришло Вам по ошибке обратитесь к Администратору ИС.\n\n" +
                    ISSignature.get();

            gf().sendEmailMsg(client.getContactEmail(), subject, text);
        } catch (MailerException ex) {
            logger.error(ex);
            ex.printStackTrace();
        }
    }

    /**
     * Создание нового заказа.
     */
    public void newOrder() {
        logger.debug("Creating new order");

        // Смотри есть ли заказ без товаров. Если есть, делаем его активным, если нет создаем.
        Client c = gf().getClientById(getSessionBean().getSelectedClientId());

        Order order = null;
        for (Order o : gf().getOrdersByClientId(c.getId())) {
            List<OrderItem> orderItems = gf().getOrderItemsByOrderId(o.getId());
            if (orderItems.size() < 1) order = o;
        }

        if (order == null) {
            order = new Order();
            setReservationDate(order, gf());
//          Это теперь не нужно, статус PRE_SAVED устанавливается по-умолчанию
//            order.setOrderStateId(OrderState.PRE_SAVED);
            order.setClientId(getSessionBean().getSelectedClientId());
            order.setManagerId(c.getManagerId());

            //  Информация о доставке
            int deliveryTimeSpan = gf().getInt("deliveryTimeSpan");   //  Не помню что это такое
            order.setDeliveryTypeId(c.getDeliveryTypeId());
            order.setDeliveryAddress(c.getAddressDlv());
            order.setDeliveryTimeSpan(deliveryTimeSpan);
            order.setDeliverySum(0);                                //  Тут наверное надо определять реальную стоимость доставки?
            try {
                gf().saveOrder(order);
                facesInfo("Заказ сохранен");
            } catch (ValidationException ex) {
                logger.error(ex);
                ex.printStackTrace();
                ValidationResult validationResult = ex.getResult();
                for (ValidationError error : validationResult.getErrors())
                    facesError(error.toHumanString());
            } catch (CreateFailureException ex) {
                logger.error(ex);
                ex.printStackTrace();
                CreateCheckResult createCheckResult = ex.getResult();
                facesError(createCheckResult.getMsg());
            }
        }

        //  Установка значений в сессионный бин
        getSessionBean().setSelectedOrderId(order.getId());
        getSessionBean().setOrder(order);
        try {
            getSessionBean().setOrderItems(new ArrayList<OrderItem>());
        } catch (RemoveFailureException ex) {
            logger.error(ex);
            ex.printStackTrace();
            String[] objs = ex.getRemoveCheckResult().getLinkedObjects();
            for (String obj : objs) {
                facesError("Товар нельзя удалить, т.к. он связан с другими объектами.");
                break;
            }
        } catch (CreateFailureException ex) {
            logger.error(ex);
            ex.printStackTrace();
            CreateCheckResult createCheckResult = ex.getResult();
            facesError(createCheckResult.getMsg());
        } catch (ValidationException ex) {
            logger.error(ex);
            ex.printStackTrace();
            ValidationResult validationResult = ex.getResult();
            for (ValidationError error : validationResult.getErrors())
                facesError(error.toHumanString());
        } catch (SettingNotFoundException ex) {
            logger.error(ex);
            ex.printStackTrace();
            facesError(ex.getSettingName());
        }
    }

    /**
     * Удаление заказа.
     */
    public void deleteOrder() {
        try {
            Order order = gf().getOrderById(orderId);
            if (order.getOrderStateId() != OrderState.PRE_SAVED)
                facesError("Заказ уже отправлен в обработку, удалить его невозможно");

            if (hasFacesErrors()) return;

            List<OrderItem> orderItems = gf().getOrderItemsByOrderId(orderId);
            for (OrderItem orderItem : orderItems) gf().removeOrderItem(orderItem.getId());
            gf().removeOrder(orderId);
            facesInfo("Товар удален из корзины");
            // Сбрасываем корзину в сесии.
            order = new Order();
            setReservationDate(order, gf());
            orderItems = new ArrayList<OrderItem>();
            getSessionBean().setSelectedOrderId(-1);
            getSessionBean().setOrder(order);
            getSessionBean().setOrderItems(orderItems);

        } catch (CreateFailureException ex) {
            logger.error(ex);
            ex.printStackTrace();
            CreateCheckResult createCheckResult = ex.getResult();
            facesError(createCheckResult.getMsg());
        } catch (ValidationException ex) {
            logger.error(ex);
            ex.printStackTrace();
            ValidationResult validationResult = ex.getResult();
            for (ValidationError error : validationResult.getErrors())
                facesError(error.toHumanString());
        } catch (SettingNotFoundException ex) {
            logger.error(ex);
            ex.printStackTrace();
            facesError(ex.getSettingName());
        } catch (RemoveFailureException ex) {
            logger.error(ex);
            ex.printStackTrace();
            String[] objs = ex.getRemoveCheckResult().getLinkedObjects();
//            for (String obj : objs) {
            facesError("Товар нельзя удалить, т.к. он связан с другими объектами.");
//            }
        } catch (InvalidIdException ex) {
            logger.error(ex);
            ex.printStackTrace();
            facesError("Указанная позиция заказа отсутствует");
        }
    }


    /**
     * -------------------------------------------------------------------------
     * ----------Валидация----------
     * -------------------------------------------------------------------------
     */

    /**
     * Проверка поля e-mail.
     *
     * @param context
     * @param component
     * @param obj
     */
    public void validateEmail(FacesContext context, UIComponent component, Object obj) {
        remoteAddr = ((javax.servlet.http.HttpServletRequest) context.getExternalContext().getRequest()).getRemoteAddr();
        String clientE_Mail = obj.toString();
        if (!clientE_Mail.equals("")) {
            Matcher matcher = email.matcher(clientE_Mail);
            if (!matcher.matches()) {
                String field = "";
                if (component.getId().equals("tbBossEmail")) field = "Email руководителя";
                if (component.getId().equals("tbContactEmail")) field = "Email менеджера";
                if (component.getId().equals("tbBuhEmail")) field = "Email бухгалтера";
                facesError("Поле " + field + " заполнено не верно.");
            }
        }
    }

    /**
     * Проверка поля сайт.
     *
     * @param context
     * @param component
     * @param obj
     */
    public void validateUrl(FacesContext context, UIComponent component, Object obj) {
        /*String clientUrl = obj.toString();
        if (!clientUrl.equals("")) {
            Matcher matcher = urlSite.matcher(clientUrl);
            if (!matcher.matches()) {
                facesError("Поле сайт заполнено не верно.");
            }
        }*/
    }

    /**
     * Проверка поля телефон.
     *
     * @param context
     * @param component
     * @param obj
     */
    public void validatePhone(FacesContext context, UIComponent component, Object obj) {
        String clientPhone = obj.toString();
        if (!clientPhone.equals("")) {
            Matcher matcher = phone.matcher(clientPhone);
            if (!matcher.matches()) {
                String field = "";
                if (component.getId().equals("tbBossPhoneWork")) field = "Рабочий телефон руководителя";
                if (component.getId().equals("tbBossPhoneMob")) field = "Мобильный телефон руководителя";
                if (component.getId().equals("tbContactPhoneWork")) field = "Рабочий телефон менеджера";
                if (component.getId().equals("tbContactPhoneMob")) field = "Мобильный телефон менеджера";
                if (component.getId().equals("tbBuhPhoneWork")) field = "Рабочий телефон бухгалтера";
                if (component.getId().equals("tbBuhPhoneMob")) field = "Мобильный телефон бухгалтера";
                facesError("Поле " + field + " заполнено не верно.");
            }
        }
    }

    //
    //  SECTION Методы для работы с общей информацией и персональной информацией
    //

    /**
     * Получить персональную информацию для текущего клиента
     */
    public String getClientPersonalInfo() {
        Client c = getClient();
        if (c == null) return "";

        return c.getInfoText();
    }

    /**
     * Получить общую информацию для клиентов
     */
    public String getClientGenericInfo() {
        Client c = getClient();
        if (c == null) return "";

        TextBlock textBlock = gf().getTextBlockByKey("clients.generic.info");
        if (textBlock == null) return "";
        else return textBlock.getValue();
    }

    //
    //  ----------------------------------------------------------------------------
    //

    public String getClientManagerMail() {
        logger.debug("getClientManagerMail");
        Client c = getClient();
        assert c != null;

        if (c.getManagerId() != null) {
            long managerId = c.getManagerId();
            User user = gf().getUserById(managerId);
            assert user != null;

            logger.debug("u " + user.getEmail());
            return user.getEmail();
        } else return "<client has no manager>";
    }

    private Object showOrdersByDateBegdate;

    private Object showOrdersByDateEnddate;

    public void showOrdersByDate() {
        getSessionBean().getOrdersFilter().setBegDateAsJavaUtil((Date) showOrdersByDateBegdate);
        getSessionBean().getOrdersFilter().setEndDateAsJavaUtil((Date) showOrdersByDateEnddate);
    }

    public Object getShowOrdersByDateBegdate() {
        return showOrdersByDateBegdate;
    }

    public void setShowOrdersByDateBegdate(Object showOrdersByDateBegdate) {
        this.showOrdersByDateBegdate = showOrdersByDateBegdate;
    }

    public Object getShowOrdersByDateEnddate() {
        return showOrdersByDateEnddate;
    }

    public void setShowOrdersByDateEnddate(Object showOrdersByDateEnddate) {
        this.showOrdersByDateEnddate = showOrdersByDateEnddate;
    }
}
